// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 计划9目录封送。见简介（5）。

package syscall

import "errors"

var (
	ErrShortStat = errors.New("stat buffer too short")
	ErrBadStat   = errors.New("malformed stat buffer")
	ErrBadName   = errors.New("bad character in file name")
)

// Qid表示9P服务器对文件的唯一标识。
type Qid struct {
	Path uint64 // 文件服务器对文件的唯一标识
	Vers uint32 // 给定路径的版本号
	Type uint8  // 文件类型（例如syscall.QTDIR）
}

// Dir包含文件的元数据。
type Dir struct {
	// 系统修改数据
	Type uint16 // 服务器类型
	Dev  uint32 // 服务器子类型

	// 文件数据
	Qid    Qid    // 来自服务器的唯一id 
	Mode   uint32 // 权限
	Atime  uint32 // 上次读取时间
	Mtime  uint32 // 上次写入时间
	Length int64  // 文件长度
	Name   string // 上次路径的元素
	Uid    string // 所有者名称
	Gid    string // 组名称
	Muid   string // 最后一个修饰符名称
}

var nullDir = Dir{
	Type: ^uint16(0),
	Dev:  ^uint32(0),
	Qid: Qid{
		Path: ^uint64(0),
		Vers: ^uint32(0),
		Type: ^uint8(0),
	},
	Mode:   ^uint32(0),
	Atime:  ^uint32(0),
	Mtime:  ^uint32(0),
	Length: ^int64(0),
}

// Null将特殊的“不接触”值分配给d的成员
// 避免在syscall.Wstat期间对其进行修改。
func (d *Dir) Null() { *d = nullDir }

// 封送将与d相对应的9P stat消息编码到b 
// 
// 如果b中没有足够的空间容纳stat消息，则返回ErrShortStat。
func (d *Dir) Marshal(b []byte) (n int, err error) {
	n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
	if n > len(b) {
		return n, ErrShortStat
	}

	for _, c := range d.Name {
		if c == '/' {
			return n, ErrBadName
		}
	}

	b = pbit16(b, uint16(n)-2)
	b = pbit16(b, d.Type)
	b = pbit32(b, d.Dev)
	b = pbit8(b, d.Qid.Type)
	b = pbit32(b, d.Qid.Vers)
	b = pbit64(b, d.Qid.Path)
	b = pbit32(b, d.Mode)
	b = pbit32(b, d.Atime)
	b = pbit32(b, d.Mtime)
	b = pbit64(b, uint64(d.Length))
	b = pstring(b, d.Name)
	b = pstring(b, d.Uid)
	b = pstring(b, d.Gid)
	b = pstring(b, d.Muid)

	return n, nil
}

// UnmarshalDir解码来自b的单个9P stat消息并返回结果Dir。
// 
// 如果b太小，无法保存有效的stat消息，则返回ErrShortStat。
// 
// 如果stat消息本身无效，则返回ErrBadStat。
func UnmarshalDir(b []byte) (*Dir, error) {
	if len(b) < STATFIXLEN {
		return nil, ErrShortStat
	}
	size, buf := gbit16(b)
	if len(b) != int(size)+2 {
		return nil, ErrBadStat
	}
	b = buf

	var d Dir
	d.Type, b = gbit16(b)
	d.Dev, b = gbit32(b)
	d.Qid.Type, b = gbit8(b)
	d.Qid.Vers, b = gbit32(b)
	d.Qid.Path, b = gbit64(b)
	d.Mode, b = gbit32(b)
	d.Atime, b = gbit32(b)
	d.Mtime, b = gbit32(b)

	n, b := gbit64(b)
	d.Length = int64(n)

	var ok bool
	if d.Name, b, ok = gstring(b); !ok {
		return nil, ErrBadStat
	}
	if d.Uid, b, ok = gstring(b); !ok {
		return nil, ErrBadStat
	}
	if d.Gid, b, ok = gstring(b); !ok {
		return nil, ErrBadStat
	}
	if d.Muid, b, ok = gstring(b); !ok {
		return nil, ErrBadStat
	}

	return &d, nil
}

// pbit8将8位数字v复制到b，并返回b的剩余部分。
func pbit8(b []byte, v uint8) []byte {
	b[0] = byte(v)
	return b[1:]
}

// pbit16将16位数字v以小端顺序复制到b，并返回b的剩余部分。
func pbit16(b []byte, v uint16) []byte {
	b[0] = byte(v)
	b[1] = byte(v >> 8)
	return b[2:]
}

// pbit32将32位数字v以小端顺序复制到b，并返回b的剩余部分。
func pbit32(b []byte, v uint32) []byte {
	b[0] = byte(v)
	b[1] = byte(v >> 8)
	b[2] = byte(v >> 16)
	b[3] = byte(v >> 24)
	return b[4:]
}

// pbit64将64位数字v以小端顺序复制到b，并返回b的剩余部分。
func pbit64(b []byte, v uint64) []byte {
	b[0] = byte(v)
	b[1] = byte(v >> 8)
	b[2] = byte(v >> 16)
	b[3] = byte(v >> 24)
	b[4] = byte(v >> 32)
	b[5] = byte(v >> 40)
	b[6] = byte(v >> 48)
	b[7] = byte(v >> 56)
	return b[8:]
}

// pstring将字符串s复制到b，以小尾端顺序在其前面加上16位长度，并返回b的剩余部分。。
func pstring(b []byte, s string) []byte {
	b = pbit16(b, uint16(len(s)))
	n := copy(b, s)
	return b[n:]
}

// gbit8从b读取一个8位数字，并将其与b的剩余部分一起返回。
func gbit8(b []byte) (uint8, []byte) {
	return uint8(b[0]), b[1:]
}

// gbit16从b中以小尾数顺序读取16位数字，并将其与b的剩余部分一起返回。
// go:nosplit 
func gbit16(b []byte) (uint16, []byte) {
	return uint16(b[0]) | uint16(b[1])<<8, b[2:]
}

// gbit32从b读取一个以小尾端顺序排列的32位数字，并将其与b的剩余部分一起返回。
func gbit32(b []byte) (uint32, []byte) {
	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
}

// gbit64从b以小尾数顺序读取64位数字，并将其与b的剩余部分一起返回。
func gbit64(b []byte) (uint64, []byte) {
	lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
	hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
	return uint64(lo) | uint64(hi)<<32, b[8:]
}

// gstring从b读取一个字符串，前缀为16位长度的小端顺序。
// 它返回包含剩余b片段和布尔值的字符串。如果长度
// 大于b中的字节数，则布尔值将为false。
func gstring(b []byte) (string, []byte, bool) {
	n, b := gbit16(b)
	if int(n) > len(b) {
		return "", b, false
	}
	return string(b[:n]), b[n:], true
}
